#include "pch.h"

#include <cstdio>

#include "CCGen.h"
#include "Math.h"

void createTestCubemap(SCubeMap *outCubemap, int width, int height, bool a = true)
{
	outCubemap->width = width;
	outCubemap->height = height;
	outCubemap->mipCount = (unsigned int)CGen::log2((float)width)+1;
	outCubemap->pixelFormat = ePF_R8G8BA8;
	outCubemap->faces = new SCubeFace[outCubemap->mipCount * 6];

	unsigned int colors[] =
	{
		0xffff0000,
		0xff00ff00,
		0xff0000ff,
		0xff00ffff,
		0xffff00ff,
		0xffffff00,
	};

	for (int i_face = 0, i_surface = 0; i_face < 6; i_face++)
	{
		unsigned int mip_width = outCubemap->width;
		unsigned int mip_height = outCubemap->width;

		for (unsigned int i_level = 0; i_level < outCubemap->mipCount; i_level++)
		{
			unsigned int pixel_count = mip_width * mip_height;
			unsigned int surface_bytewidth = pixel_count * sizeof(__int32);

			SCubeFace &curSurface = outCubemap->faces[i_surface++];

			curSurface.dataPtr = _aligned_malloc(surface_bytewidth, 16);
			curSurface.dataSize = surface_bytewidth;
			curSurface.width = mip_width;
			curSurface.height = mip_height;

			unsigned __int32 *pixelPtr = (unsigned __int32*)curSurface.dataPtr;

			if (a)
			{
				for (unsigned int i_x = 0; i_x < mip_width; i_x++)
				{
					for (unsigned int i_y = 0; i_y < mip_width; i_y++)
					{
						*pixelPtr = colors[i_face];
						pixelPtr++;
					}
				}
			}

			assert(mip_width > 0 && mip_height > 0);

			mip_width = mip_width >> 1;
			mip_height = mip_height >> 1;
		}
	}

}

enum DXGI_FORMAT;
enum D3D10_RESOURCE_DIMENSION;

struct DDS_PIXELFORMAT {
	unsigned int dwSize;
	unsigned int dwFlags;
	unsigned int dwFourCC;
	unsigned int dwRGBBitCount;
	unsigned int dwRBitMask;
	unsigned int dwGBitMask;
	unsigned int dwBBitMask;
	unsigned int dwABitMask;
};

typedef struct {
	unsigned int           dwSize;
	unsigned int           dwFlags;
	unsigned int           dwHeight;
	unsigned int           dwWidth;
	unsigned int           dwPitchOrLinearSize;
	unsigned int           dwDepth;
	unsigned int           dwMipMapCount;
	unsigned int           dwReserved1[11];
	DDS_PIXELFORMAT ddspf;
	unsigned int           dwCaps;
	unsigned int           dwCaps2;
	unsigned int           dwCaps3;
	unsigned int           dwCaps4;
	unsigned int           dwReserved2;
} DDS_HEADER;


typedef struct {
	DXGI_FORMAT              dxgiFormat;
	D3D10_RESOURCE_DIMENSION resourceDimension;
	unsigned int             miscFlag;
	unsigned int             arraySize;
	unsigned int             miscFlags2;
} DDS_HEADER_DXT10;

void load_cubemap(SCubeMap *outCubemap, char *filename, char **p1, int *p2)
{
	//Do not try this at home

	FILE *texture_file;

	fopen_s(&texture_file, filename, "rb");

	if (texture_file)
	{
		DDS_HEADER ddsHeader;

		*p2 = 4 + sizeof(DDS_HEADER);
		*p1 = new char[*p2];

		fread((*p1), 4, 1, texture_file);

		fread(&ddsHeader, sizeof(DDS_HEADER), 1, texture_file);
		memcpy((*p1) + 4, &ddsHeader, sizeof(DDS_HEADER));

		//fread(&dds10Header, sizeof(DDS_HEADER_DXT10), 1, texture_file);
		//memcpy((*p1) + 4 + sizeof(DDS_HEADER), &dds10Header, sizeof(DDS_HEADER_DXT10));

		//ddsHeader.dwMipMapCount = CGen::max(1u, ddsHeader.dwMipMapCount);

		outCubemap->width = ddsHeader.dwWidth;
		outCubemap->height = ddsHeader.dwHeight;
		outCubemap->mipCount = std::max(ddsHeader.dwMipMapCount, 1U);
		outCubemap->pixelFormat = ePF_R8G8BA8;
		outCubemap->faces = new SCubeFace[6 * outCubemap->mipCount];

		for (int i_face = 0, i_surface = 0; i_face < 6; i_face++)
		{
			unsigned int mipWidth = ddsHeader.dwWidth;
			unsigned int mipHeight = ddsHeader.dwHeight;

			for (unsigned int i_mip = 0; i_mip < outCubemap->mipCount; i_mip++)
			{
				SCubeFace &curFace = outCubemap->faces[i_surface];

				curFace.width = mipWidth;
				curFace.height = mipHeight;
				curFace.dataSize = mipHeight * mipWidth * 4;
				curFace.dataPtr = _aligned_malloc(curFace.dataSize, 16);

				fread_s(curFace.dataPtr, curFace.dataSize, curFace.dataSize, 1, texture_file);
				i_surface++;

				mipWidth = mipWidth >> 1;
				mipHeight = mipHeight >> 1;
			}
		}

		fclose(texture_file);
	}

}

CCGen g_filter;

int main()
{

	SFilterStateDesc sd;
	
	sd.cosPowerDrop = 0.25f;
	sd.srcSize = 256;
	sd.dstSize = 256;
	sd.fixupType = CP_FIXUP_WARP;
	sd.glossBias = 0;
	sd.glossScale = 0;
	sd.lightingModel = CP_LIGHTINGMODEL_PHONG;
	sd.mipMode = CP_COSINEPOWER_CHAIN_DROP;
	sd.specularPower = 2048;
	sd.dstPixelFormat = ePF_R8G8BA8;
	sd.srcPixelFormat = ePF_R8G8BA8;

	SFilterState *fs;
	
	g_filter.EnableGpu();
	g_filter.Init();

	g_filter.CreateFilterState(&sd, &fs);
	
	SFilterJob newJob;
	
	newJob.filterState = fs;
	newJob.inputCubemap = 0;
	newJob.outputCubemap = 0;
	
	SCubeMap input;
	SCubeMap *output;
	
	createTestCubemap(&input, 256, 256);
	//createTestCubemap(&output, 256, 256, false);
	
	SCubeMap cubemap;
	
	char *dds_header;
	int header_size;
	
	load_cubemap(&cubemap, "Water256.dds", &dds_header, &header_size);
	//cubemap.mipCount = 1;
	newJob.inputCubemap = &cubemap;
	newJob.outputCubemap = &output;
	
	LARGE_INTEGER t0, t1, freq;
	
	while (1)
	{
		g_filter.AddFilterJob(&newJob);
		QueryPerformanceFrequency(&freq);
		QueryPerformanceCounter(&t0);

		g_filter.WaitForAll();

		Sleep(10);
	}

	QueryPerformanceCounter(&t1);
	
	float dt = (float)((double)(t1.QuadPart - t0.QuadPart)) / freq.QuadPart;
	
	printf("Filtering time %f", dt);
	
	FILE *texture_file;
	
	fopen_s(&texture_file, "test.dds", "wb");
	
	fwrite(dds_header, header_size, 1, texture_file);
	
	for (int i_array = 0, i_surface = 0; i_array < 6; i_array++)
	{
		for (unsigned int i_level = 0; i_level < output->mipCount; i_level++)
		{
			const SCubeFace &curFace = output->faces[i_surface++];
	
			fwrite(curFace.dataPtr, curFace.dataSize, 1, texture_file);
		}
	}
	
	fclose(texture_file);
	
	return 0;
}

bool keep = true;

void test(void *userPtr)
{
	keep = false;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
	SFilterStateDesc sd;

	sd.cosPowerDrop = 0.25f;
	sd.srcSize = 256;
	sd.dstSize = 256;
	sd.fixupType = CP_FIXUP_WARP;
	sd.glossBias = 0;
	sd.glossScale = 0;
	sd.lightingModel = CP_LIGHTINGMODEL_PHONG;
	sd.mipMode = CP_COSINEPOWER_CHAIN_DROP;
	sd.specularPower = 2048;
	sd.dstPixelFormat = ePF_R8G8BA8;
	sd.srcPixelFormat = ePF_R8G8BA8;

	SFilterState *fs;

	g_filter.EnableGpu();
	g_filter.Init();

	g_filter.CreateFilterState(&sd, &fs);

	SFilterJob newJob;

	newJob.filterState = fs;
	newJob.inputCubemap = 0;
	newJob.outputCubemap = 0;
	newJob.onCompleteCallback = test;
	newJob.userPtr = "test";

	SCubeMap input;
	SCubeMap *output;

	createTestCubemap(&input, 256, 256);
	//createTestCubemap(output, 256, 256, false);

	SCubeMap cubemap;

	char *dds_header;
	int header_size;

	load_cubemap(&cubemap, "_Water256.dds", &dds_header, &header_size);
	newJob.inputCubemap = &cubemap;
	newJob.outputCubemap = &output;

	LARGE_INTEGER t0, t1, freq;

	QueryPerformanceCounter(&t0);
	QueryPerformanceFrequency(&freq);

	g_filter.AddFilterJob(&newJob);

	while (keep)
	{
		QueryPerformanceCounter(&t1);

		float dt = (float)((double)(t1.QuadPart - t0.QuadPart)) / freq.QuadPart;

		if (dt > 1)
		{
			//keep = false;
		}

		//g_filter.AddFilterJob(&newJob);

		//g_filter.WaitForAll();

		Sleep(10);
	}

	FILE *texture_file;

	fopen_s(&texture_file, "test.dds", "wb");

	fwrite(dds_header, header_size, 1, texture_file);

	for (int i_array = 0, i_surface = 0; i_array < 6; i_array++)
	{
		for (unsigned int i_level = 0; i_level < output->mipCount; i_level++)
		{
			const SCubeFace &curFace = output->faces[i_surface++];

			fwrite(curFace.dataPtr, curFace.dataSize, 1, texture_file);
		}
	}

	fclose(texture_file);
}